home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 1992 August
/
info-mac-1992.iso
/
Applications (app)
/
DaliClock
/
Clock.asm
< prev
next >
Wrap
Assembly Source File
|
1987-02-23
|
15KB
|
614 lines
; This is Steve Capp's clock program, written in the early days of Macintosh.
; The original program had every kind of display dependency: base address,
; rowbytes, and bounds. As an added bonus, the code segment was self-
; modifying. (The _code_ wasn't self-modifying, but the code segment had
; an embedded variable area.) I disassembled it with MacNosy, fixed all(?)
; the compatibility bugs, threw in a few optimizations, and here it is.
; Somewhere along the way, I lost most of it: the code size (bytes in the
; CODE 1 resource) dropped from 3478 bytes to less than 1100.
; I've cleared out most of the "magic numbers" from the code, but I still
; don't understand how the drawing works. Good luck.
; Tested on a Mac Plus and a MegaScreen.
; Ephraim Vishniac
; February 22, 1987
; uucp: decvax!wanginst!wang!ephraim
; MCI mail: masstech
; US snail: P.O. Box 1357
; East Arlington, MA 02174
; Miscellaneous things I discovered:
; When the mouse is below row 280 on the screen, an informative
; message is displayed. In this version, the string displayed
; depends on the the mouse column (horizontal coordinate) reduced
; modulo the number of strings.
;
; Pressing the mouse while the cursor is above row 280 causes the
; date to be displayed instead of the time. Pressing the mouse
; while the cursor is below row 280 exits to the shell.
;
; To prevent screen burn-in, the digits move down and up through
; a range of 75 rows. The rate of motion is one row every sixteen
; seconds.
Include Traps.D ; Use System and ToolBox traps
Include SysEquX.D ; Use System equates
Include ToolEqu.D ; Use ToolBox equates
Include QuickEquX.D ; Use QuickDraw equates
; Some equates
qdGlobals equ -4 ; qdGlobals are at -4(A5)
NumNum equ 6 ; six numerals
NumWidth equ 8 ; numeral is eight bytes wide (64 pixels)
DispWidth equ NumNum*NumWidth ; width of display area (bytes)
DispRows equ 72 ; display is 72 rows high
DispBytes equ DispWidth*DispRows
Steps equ 9 ; Nine steps in transformation
WorkBytes equ DispBytes*Steps ; size of work area
StringBase equ 1000 ; first 'STR ' resource
NumStrings equ 4 ; number of 'STR ' resources
StartRow equ 60 ; initial row for clock
CursorBound equ 280 ; cursor transition is at row 280
MessageRow equ CursorBound+30 ; Starting row for announcements
; Globals
MouseRow ds.w 1 ; MousePoint.v
MouseCol ds.w 1 ; MousePoint.h
ShowDate ds.b 1 ; 0 = Show Time, 1 = Show Date
LowCursor ds.b 1 ; cursor is low on screen
Year ds.w 1 ; This is a date-time record
Month ds.w 1 ;
Day ds.w 1 ;
Hour ds.w 1 ;
Minute ds.w 1 ;
Second ds.w 1 ;
WeekDay ds.w 1 ; (not used by us, but set by _Secs2Date)
MyPort ds.b portRec ; our GrafPort record
MyBounds equ MyPort+portBits+bounds
MyTopLeft equ MyBounds+topLeft
MyTop equ MyTopLeft+v
MyLeft equ MyTopLeft+h
MyBotRight equ MyBounds+botRight
MyBottom equ MyBotRight+v
MyRight equ MyBotRight+h
MyBaseAddr equ MyPort+portBits+baseAddr
MyRowBytes equ MyPort+portBits+rowBytes
TrendDown ds.b 1 ; 1 = Down, 0 = Up
PaintPtr ds.l 1 ; screen address to start painting
OldData ds.l NumNum ; previous date or time, expanded
NewData ds.l NumNum ; new date or time, expanded
WorkArea ds.l 1 ; pointer to work area
glob39 ds.l NumNum ; pointers to mask resource (RAWB 20)
glob46 ds.l 10 ; pointers to numeral data (RAWB 1-10)
MyTempRect ds.w 4 ; rectangle used in CHECKCUR
; Start here!
MAIN
JSR INIT
; Calculate starting point for painting the screen
MOVEQ #StartRow,D0 ; rows down from top
mulu MyRowBytes(A5),D0 ; times bytes-per-row
ADD.L MyBaseAddr(A5),D0 ; plus start of bitmap
move.w MyRight(A5),D1 ; D1.W = right bound
sub.w MyLeft(A5),D1 ; D1.W = screen width
sub.w #DispWidth*8,D1 ; width of our drawing (pixels)
lsr.w #4,D1 ; divide by two to center,
ext.l D1 ; and by eight for bytes
add.l D1,D0 ; center the clock
MOVE.L D0,PaintPtr(A5)
; Blackout!
_HideCursor ; put the cursor away
; blacken the entire screen
PEA MyBounds(A5) ; rectangle for screen
PEA qdGlobals+black(a5) ; fill with black
_FillRect
; One-time setup for main loop
JSR GetTime
JSR SetData
Moveq #NumNum-1,D0 ; copy NewData to OldData
LEA NewData(A5),A0
LEA OldData(A5),A1
@1 MOVE.L (A0)+,(A1)+
dbra D0,@1
CLR.B TrendDown(A5) ; establish a trend
BRA @2 ; hop into loop
; This is the top of the main loop
@3 move.L Time,D6 ; D6 = old time
JSR MELT
; Wait for the second to change
@4 cmp.L Time,D6 ; compare old time to current time
beq @4 ; loop until the seconds change
JSR GetTime ; and then get the full time
JSR SetData ; convert time/date to data
; The following stuff moves the image up or down by one row
; every sixteen seconds to avoid screen burn-in.
@6 MOVE.B Time+3,D0 ; Get low order byte of time
and.b #$F,D0 ; every 16 secs, shift
BNE @9 ; branch if non-zero, don't shift
MOVE.W MyRowBytes(A5),D0
EXT.L D0 ; D0.L = bytes per row
TST.B TrendDown(A5) ; Should we move up or down?
BEQ @7 ; branch if up
ADD.L D0,PaintPtr(A5) ; move down one row
BRA @8
@7 SUB.L D0,PaintPtr(A5) ; move up one row
@8 DBRA D7,@9 ; every once in a while, switch
@2 MOVE.W #DispRows+2,D7 ; recharge the trend counter
BCHG.B #0,TrendDown(A5) ; flip the trend flag
@9 JSR CHECKCUR ; check the mouse position
PEA glob46(A5) ; pointers to numeral data
PEA OldData(A5) ; old time or date
PEA NewData(A5) ; new time or date
Move.L WorkArea(A5),-(A7) ; pointer to work area
PEA glob39(A5) ; offsets into mask resource
JSR PROC30 ; build new images
; If Button, Show Date
; If Not Button, Show Time
; If LowCursor & Button, Fall out
CLR -(A7)
_Button ; (A7) = Button
MOVE.B (A7),ShowDate(A5) ; If button, Show Date
MOVE.B LowCursor(A5),D0
AND.B (A7)+,D0 ; D0 = LowCursor & Button
BEQ @3
RTS ; Exit to wherever we came from...
StringPtr set 8 ; single parameter for ANNOUNCE
;-refs - CHECKCUR
ANNOUNCE
LINK A6,#-0
move.w #SrcBic,-(A7)
_TextMode
CLR -(A7) ; for string width
MOVE.L StringPtr(A6),-(A7) ; string pointer
_StringWidth ; (A7) = string width
move.w MyRight(A5),D1 ; D1.W = right bound
sub.w MyLeft(A5),D1 ; D1.W = screen width
SUB.W (A7)+,D1 ; D1.W = excess screen width
LSR.W #1,D1 ; D1 = half of excess screen width
move.w D1,-(A7)
move.w #MessageRow,-(A7) ; row for drawing text
_MoveTo
MOVE.L StringPtr(A6),-(A7) ; string pointer
_DrawString
UNLK A6
Move.L (A7)+,(A7) ; remove parameter
RTS
;-refs - MELT MAIN
CHECKCUR
PEA MouseRow(A5)
_GetMouse
CMPI #CursorBound,MouseRow(A5)
BLE @4 ; branch if mouse in upper screen
BSET.B #0,LowCursor(A5) ; mouse is low now
BNE @3 ; skip if it was already
_ShowCursor ; else show cursor and message
CLR.L -(A7) ; for string handle
move.w MouseCol(A5),D0 ; Get mouse column
ext.l D0
divu #NumStrings,D0 ; equal chance of each message
swap D0 ; D0.W = remainder
add.w #StringBase,D0 ; D0.W = STR resource number
move.w D0,-(A7) ; string #
_GetString
move.L (A7)+,A0 ; string handle
move.L (A0),-(A7) ; string pointer
JSR ANNOUNCE
@3 BRA @5
@4 BCLR.B #0,LowCursor(A5) ; Mouse is high now
BEQ @5 ; skip if it was already
_HideCursor ; else hide cursor and message
LEA MyTempRect(A5),A1 ; A1 = our rectangle
Move.L A1,-(A7) ; push for _FillRect
move.w #CursorBound,(A1)+ ; top of rect
move.w MyLeft(a5),(A1)+ ; left of screen
move.l MyBotRight(a5),(A1)+ ; bottom right of screen
PEA qdGlobals+black(a5) ; fill with black
_FillRect
@5 RTS
;-refs - MAIN
INIT
; Build array of pointers to RAWB 1-10
CLR D7
LEA glob46(A5),A3
@1 ADDQ #1,D7
CLR.L -(A7)
move.L #'RAWB',-(A7)
move.w D7,-(A7)
_GetResource
move.L (A7)+,A0
MOVE.L (A0),(A3)+
CMPI #9,D7
BLE @1
CLR.L -(A7)
move.L #'RAWB',-(A7)
move.w #20,-(A7)
_GetResource
move.L (A7)+,A0
MOVE.L (A0),A4 ; RAWB 20 pointer
LEA 64(A4),A3
LEA glob39(A5),A0 ; glob39[0]
MOVE.L A3,(A0)+ ; glob39[0]
MOVE.L A4,(A0)+ ; glob39[1]
MOVE.L A3,(A0)+ ; glob39[2]
MOVE.L A4,(A0)+ ; glob39[3]
MOVE.L A3,(A0)+ ; glob39[4]
MOVE.L A4,(A0)+ ; glob39[5]
move.L #WorkBytes,D0 ; space for building images
_NewPtr
move.L A0,WorkArea(a5) ; start of allocated memory
CLR.B LowCursor(A5)
CLR.B ShowDate(A5)
PEA qdGlobals(A5)
_InitGraf
PEA MyPort(A5)
_OpenPort
_InitCursor
_InitFonts
move.w #applFont,-(A7) ; use application font
_TextFont
move.w #18,-(A7) ; in 18 point size
_TextSize
RTS
MELTTICKS equ 4 ; ticks to delay in MELT
;-refs - MAIN
MELT
MOVEM.L D6-D7/A4,-(A7)
moveq #Steps-1,D7
Move.L WorkArea(A5),A4 ; A4 = work area
@1 MOVE.L #MELTTICKS,D6 ; D6 = ticks to delay
ADD.L Ticks,D6 ; D6 = tick time to delay until
move.L A4,-(A7) ; push pointer to painting area
TST.B ShowDate(A5) ; Show Date?
BEQ @2 ; branch if not
JSR PROC34 ; paint squares for showing date
BRA @3
@2 JSR PROC33 ; paint circles for showing time
@3 move.L A4,-(A7) ; push pointer into work area
move.L PaintPtr(A5),-(A7) ; push pointer to screen image
JSR PROC31 ; copy image to screen
@4 IF MELTTICKS>1
JSR CHECKCUR
ENDIF
CMP.L Ticks,D6 ; Time to go on yet?
BGT @4 ; Loop if not
LEA DispBytes(A4),A4 ; next image in work area
dbra D7,@1
moveq #NumNum-1,D7
LEA NewData(A5),A0
LEA OldData(A5),A1
@8 MOVE.L (A0)+,(A1)+
DBRA D7,@8
MOVEM.L (A7)+,D6-D7/A4
RTS
;-refs - MAIN
SetData
lea Year(A5),A0 ; input area
TST.B ShowDate(A5) ; Show date?
bne @1 ; skip if so
lea Hour(A5),A0 ; else show time
@1 LEA NewData(A5),A1 ; output area
moveq #2,D1 ; 3 fields to process
@0 move (a0)+,D0 ; D0 = field to process
ext.l D0 ; clear high word
divu #10,D0 ; divide by ten
clr.w (a1)+ ; clear high word of output
move.w D0,(A1)+ ; set decades
swap D0
clr.w (A1)+ ; clear high word of output
move.w D0,(A1)+ ; set units
dbra D1,@0
RTS
;-refs - MAIN
param1 set 76 ; numeral data (glob46)
param2 set 72 ; old data (OldData)
param3 set 68 ; new data (NewData)
param4 set 64 ; work area (WorkArea)
param5 set 60 ; mask table pointers (glob39)
paramsize set 20
PROC30
move.L (A7)+,A0 ; A0 = return address
MOVEM.L D0-D7/A0-A6,-(A7) ; sixty bytes onto stack
Move.L param4(A7),A5 ; A5 = start of work area
Lea (NumNum-1)*NumWidth(A5),A3 ; offset to right-most digit
MOVE #20,D0 ; index to unit seconds or days
@1 MOVEA.L param1(A7),A4 ; A4 = base of numeral data ptr array
MOVEA.L param2(A7),A5 ; old data
MOVE.L 0(A5,D0.W),D6 ; D6 = old digit
LSL #2,D6
MOVEA.L 0(A4,D6.W),A1 ; A1 = data for old digit
MOVEA.L param3(A7),A5 ; new data
MOVE.L 0(A5,D0.W),D6 ; D6 = new digit
LSL #2,D6
MOVEA.L 0(A4,D6.W),A2 ; A2 = data for new digit
MOVEA.L param5(A7),A5
MOVEA.L 0(A5,D0.W),A0 ; A0 = ptr to RAWB 20 (perhaps +64)
MOVE #DispRows-1,D1
@2 MOVEA.L A3,A4
MOVE (A1)+,D2
MOVE (A1)+,D3
MOVE (A2)+,D4
MOVE (A2)+,D5
ADDI #15,D5
SUB D2,D4
ASR #3,D4
SUB D3,D5
ASR #3,D5
MOVEQ #Steps-1,D7
@3 MOVE D2,D6
LSR #1,D6
ANDI #$FFF8,D6
MOVEA.L A0,A5
ADDA.W D6,A5
MOVE D3,D6
LSR #1,D6
ANDI #$FFF8,D6
MOVEA.L A0,A6
ADDA.W D6,A6
MOVE.L (A6)+,D6
NOT.L D6
AND.L (A5)+,D6
MOVE.L D6,(A4)+
MOVE.L (A6)+,D6
NOT.L D6
AND.L (A5)+,D6
MOVE.L D6,(A4)+
ADD D4,D2
ADD D5,D3
ADDA.W #DispBytes-NumWidth,A4 ; next image
DBRA D7,@3
MOVEA.L A3,A4
MOVE (A1)+,D2
MOVE (A1)+,D3
MOVE (A2)+,D4
MOVE (A2)+,D5
ADDI #15,D5
SUB D2,D4
ASR #3,D4
SUB D3,D5
ASR #3,D5
MOVEQ #Steps-1,D7
@4 MOVE D2,D6
LSR #1,D6
ANDI #$FFF8,D6
MOVEA.L A0,A5
ADDA.W D6,A5
MOVE D3,D6
LSR #1,D6
ANDI #$FFF8,D6
MOVEA.L A0,A6
ADDA.W D6,A6
MOVE.L (A6)+,D6
NOT.L D6
AND.L (A5)+,D6
OR.L D6,(A4)+
MOVE.L (A6)+,D6
NOT.L D6
AND.L (A5)+,D6
OR.L D6,(A4)+
ADD D4,D2
ADD D5,D3
ADDA.W #DispBytes-NumWidth,A4 ; next image
DBRA D7,@4
ADDA.W #DispWidth,A3 ; next row
DBRA D1,@2
SUB.W #DispBytes+NumWidth,A3 ; back to top, previous numeral
SUBQ #3,D0 ; previous numeral
DBMI D0,@1
MOVEM.L (A7)+,D0-D7/A0-A6 ; restore all registers
ADDA.W #ParamSize,A7 ; clear parameters
JMP (A0)
;-refs - MELT
PROC31
move.L (A7)+,D0 ; D0 = return address
move.L (A7)+,A0 ; A0 = place to draw
move.L (A7)+,A1 ; A1 = stuff to draw
move.L D0,-(A7) ; restore return address
MOVEQ #DispRows-1,D0 ; rows in drawing
@1 move.l A0,-(A7) ; save start of row
MOVEQ #DispWidth/4-1,D1 ; width of drawing, in longs
@2 NOT.L (A1)
MOVE.L (A1)+,(A0)+
DBRA D1,@2
move.l (A7)+,A0 ; restore start of row
ADDA.W MyRowBytes(A5),A0 ; skip to next row
DBRA D0,@1
RTS
; PROC33 paints the colons which separate portions of the time
;-refs - MELT
Circle dc.w %0000001111000000
dc.w %0000011111100000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000011111100000
dc.w %0000001111000000
PROC33
move.L (A7)+,D0 ; D0 = return address
move.L (A7)+,A0 ; A0 = place to paint
LEA Circle,A1 ; A1 = Circle image
ADDA.W #22*DispWidth+2*NumWidth-1,A0
; 22 rows down, 2 chars in, back 8 pixels
MOVE #7,D2
@1 MOVE.B (A1)+,D1 ; D1.B = byte of image
MOVE.B D1,2*NumWidth(A0) ; top right (2 chars over)
MOVE.B D1,18*DispWidth(A0) ; bottom left (18 rows down)
MOVE.B D1,18*DispWidth+2*NumWidth(A0) ; bottom right (18 down, 2 over)
MOVE.B D1,(A0)+ ; top left
MOVE.B (A1)+,D1 ; D1.B = byte of image
MOVE.B D1,2*NumWidth(A0) ; top right (2 chars over)
MOVE.B D1,18*DispWidth(A0) ; bottom left (18 rows down)
MOVE.B D1,18*DispWidth+2*NumWidth(A0) ; bottom right (18 down, 2 over)
MOVE.B D1,(A0)+ ; top left
ADDA.W #DispWidth-2,A0 ; next row
DBRA D2,@1
MOVEA.L D0,A0
JMP (A0)
; PROC34 paints the squares which separate portions of the date
;-refs - MELT
Square dc.w %0000000000000000 ; actually a rectangle
dc.w %0000000000000000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000111111110000
dc.w %0000111111110000
PROC34
move.L (A7)+,D0 ; D0 = return address
move.L (A7)+,A0 ; A0 = place to paint
LEA Square,A1 ; A1 = Square
ADDA.W #32*DispWidth+2*NumWidth-1,A0
; 32 rows down, 2 chars in, 8 pixels back
MOVE #7,D2
@1 MOVE.B (A1)+,D1 ; D1.B = byte of square
MOVE.B D1,2*NumWidth(A0) ; right (2 chars right)
MOVE.B D1,(A0)+ ; left
MOVE.B (A1)+,D1 ; D1.B = byte of square
MOVE.B D1,2*NumWidth(A0) ; right (2 chars right)
MOVE.B D1,(A0)+ ; left
ADDA.W #DispWidth-2,A0 ; next row
DBRA D2,@1
MOVEA.L D0,A0
JMP (A0)
;-refs - MAIN
GetTime
move.l Time,D0
LEA Year(A5),A0
_Secs2Date
Move.W (A0),D0 ; D0 = year
Ext.L D0 ; clear high word
Divu #100,D0 ; divide by century
Swap D0 ; D0.W = year within century
Move.W D0,(A0) ; set decade+year, no centuries
RTS